/*
 * @author hzq
 * @date 2024-12-01 19:39:40
 * @description 本地存储封装,带过期时间、带前缀
 * @see 🚀
 * @status done
 */

/** 解构环境变量 */
const { VITE_STORAGE_PREFIX, VITE_CACHE_EXPIRE } = import.meta.env

/**
 * 定义存储类型，可以是localStorage或sessionStorage
 */
type StorageType = typeof localStorage | typeof sessionStorage

/**
 * Storage
 * @description 封装的本地存储类
 */
export default class Storage {
  // 私有属性，存储类型
  private storage: StorageType
  // 私有属性，前缀键
  private prefixKey: string

  private expire: number | null

  /**
   * 构造函数，初始化存储类型和前缀键
   * @param prefixKey 前缀键
   * @param storage 本地储存类型，默认为localStorage
   */
  constructor(
    prefixKey: string = '',
    storage = localStorage,
    expire: number | null = VITE_CACHE_EXPIRE
  ) {
    this.storage = storage
    this.prefixKey = prefixKey
    this.expire = expire
  }

  // 获取键，加上前缀键并转换为大写
  private getKey(key: string) {
    return `${this.prefixKey}${key}`.toUpperCase()
  }

  /**
   * 设置键值对，可以设置过期时间
   * @param key 键
   * @param value 值
   * @param expire 过期时间，单位为秒，默认为7天
   */
  set(key: string, value: any) {
    // console.log(expire)

    const data = {
      value,
      expire: this.expire != null ? Date.now() + this.expire * 1000 : null
    }
    const stringifyData = JSON.stringify(data)
    this.storage.setItem(this.getKey(key), stringifyData)
  }

  /**
   * 获取键对应的值，如果过期则删除该键
   * @param key 键
   * @param def 默认值，默认为null
   * @returns
   */
  get(key: string, def: any = null) {
    const data = this.storage.getItem(this.getKey(key))
    if (data) {
      try {
        const result = JSON.parse(data)
        const { value, expire } = result
        if (expire == null || expire >= Date.now()) {
          return value
        }
        this.remove(key)
      } catch (e) {
        return def
      }
    }
    return def
  }

  /**
   * 删除键
   * @param key 键
   */
  remove(key: string) {
    this.storage.removeItem(this.getKey(key))
  }

  /**
   * 清空所有键
   */
  clear() {
    this.storage.clear()
  }

  //#region cookie

  /**
   * 设置cookie；如果过期时间未设置，默认关闭浏览器自动删除
   * @param key 键
   * @param value 值
   * @param expire 过期时间
   */
  setCookie(
    key: string,
    value: any,
    expire: number | null = VITE_CACHE_EXPIRE
  ) {
    const maxAge = expire != null ? `; Max-Age=${expire}` : ''
    document.cookie = `${this.getKey(key)}=${value}${maxAge}`
  }

  /**
   * 根据名字获取cookie值
   * @param name 键
   * @returns
   */
  getCookie(name: string) {
    const cookies = document.cookie.split(';')
    for (const cookie of cookies) {
      const [key, value] = cookie.split('=')
      if (this.getKey(key) === name) {
        return value
      }
    }
    return ''
  }

  /**
   * 根据名字删除指定的cookie
   * @param key 键
   */
  removeCookie(key: string) {
    this.setCookie(key, '', -1)
  }

  /**
   * 清空所有cookie 使所有cookie失效
   */
  clearCookie() {
    const keys = document.cookie.match(/[^ =;]+(?==)/g)
    if (keys) {
      for (let i = keys.length; i--; ) {
        const key = keys[i]
        this.setCookie(key, '', -1)
      }
    }
  }
  //#endregion
}

/** 本地储存持久化 */
export const localCache = new Storage(VITE_STORAGE_PREFIX, localStorage)
/** 本地储存会话持久化 */
export const sessionCache = new Storage(VITE_STORAGE_PREFIX, sessionStorage)

/*
 * @author hzq
 * @date 2024-12-04 00:05:37
 * @description 事件在同一个域下的不同页面之间触发，即在 A 页面注册了 storge 的监听处理，只有在跟 A 同域名下的 B 页面操作 storage 对象，A 页面才会被触发 storage 事件
 * @see 🚀
 * @status done
 */

/**
 * 导出一个函数sendMsg，用于发送消息
 * @param type 消息类型
 * @param payload
 */
export function sendMsg(type: string, payload: any) {
  // 将消息类型和消息内容存储到localStorage中，以@@开头
  localStorage.setItem(
    '@@' + type,
    JSON.stringify({
      payload,
      time: Date.now() // 添加一个时间戳,避免相同的payload不被触发
    })
  )
}

/**
 * 导出一个函数，用于监听storage事件
 * @param handler 处理函数，接收两个参数，key和payload
 * @returns
 */
export function listenMsg(handler: (key: string, payload: any) => void) {
  // 定义一个storage事件处理函数
  const storageHandler = (e: StorageEvent) => {
    // 如果没有新值或者没有key，则返回
    if (!e.newValue || !e.key) return
    try {
      // 将新值解析为JSON对象
      const data = JSON.parse(e.newValue)
      // 调用传入的handler函数，传入key和payload
      handler(e.key.substring(2), data.payload)
    } catch (e) {
      // 如果解析失败，抛出错误
      throw new Error('storage event parse error')
    }
  }
  // 添加storage事件监听器
  window.addEventListener('storage', storageHandler)
  // 返回一个函数，用于移除事件监听器
  return () => {
    window.removeEventListener('storage', storageHandler)
  }
}
